package com.espirit.eap.manager;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;

import org.codehaus.jackson.annotate.JsonIgnore;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import com.espirit.eap.annotation.DomainModel;
import com.espirit.eap.annotation.ModelDisplayProperty;
import com.espirit.eap.pagelayout.Page;
import com.espirit.eap.util.ChineseSpeller;
import com.googlecode.cswish.annotation.Protected;
import com.googlecode.cswish.model.Saas;
import com.googlecode.cswish.struts.ContextHolder;
import com.googlecode.cswish.struts.hibernate.HibernateUtil;



@Entity
@DomainModel
@ModelDisplayProperty("name")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Menu implements Serializable, Saas{
	private static final long serialVersionUID = -7863576450925390717L;
	@JsonIgnore
	private Integer id;
	private Integer itemNbr;
	private String name;
	private String i18n;
	private List<Menu> children;
	private Menu parent;

	// the main page
	@JsonIgnore
	private Page page;
	
	// the relevant pages
	private List<Page> relevantPages;
	
	// Transient
	private String href;
	// Transient
	private String hrefTarget;
	// Transient
	private String text;
	
	private int deep;
	
	@JsonIgnore
	private Integer sid;
	public Integer getSid() {
		return sid;
	}

	public void setSid(Integer sid) {
		this.sid = sid;
	}
	
	@Id
	@GeneratedValue
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

//	@Column(length = 50, nullable = false)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	// TODO: confuse with pages
	@ManyToOne(targetEntity=Page.class,fetch=FetchType.LAZY ,cascade=CascadeType.MERGE)
	public Page getPage(){
		return this.page;
	}
	
	public void setPage(Page page) {
		this.page = page;
	}
	
	@Transient
	@JsonIgnore
	public String getFirstChildPath() {
		List<Menu> childMenus = getChildMenus();
		String firstChildPath;
		// it's new parent, no any children
		if (childMenus == null || childMenus.size() == 0) {
			if (this.getI18n() != null) {
				firstChildPath = '/' + ContextHolder.get().getSiteName() + '/' + 
						ChineseSpeller.convert(this.getI18n(), true, false) + '/' + "xxx._search.html";
			} else {
				firstChildPath = '/' + ContextHolder.get().getSiteName() + '/' + "xxx._search.html";
			}
		} else {		
			Menu subMenu = childMenus.get(0);
			Page firstChildPage = subMenu.getPage();
			if (firstChildPage == null) {
				childMenus = subMenu.getChildMenus();
				if (childMenus != null && childMenus.size() > 0) {
					firstChildPath = childMenus.get(0).getPage().getPath();
				} else {
					firstChildPath = '/' + ContextHolder.get().getSiteName() + 
							'/' + ChineseSpeller.convert(this.getI18n(), true, false) + '/' +
							ChineseSpeller.convert(subMenu.getI18n(), true, false) + "._search.html";
				}
			} else {
				firstChildPath = firstChildPage.getPath();
			}
		}
		return firstChildPath;
	}
	
//	@Column(table = "menu")
	public Integer getItemNbr() {
		return itemNbr;
	}
	public void setItemNbr(Integer itemNbr) {
		this.itemNbr = itemNbr;
	}
	@Transient
	@JsonIgnore
	public boolean isLeaf(){
		return page != null;
	}
	
	@Transient
	@JsonIgnore
	public String getHref(){
		return this.href;
	}
	public void setHref(String href){
		this.href = href;
	}
	
	@Transient
	@JsonIgnore
	public String getCls(){
		return isLeaf() ? "file" : "folder";
	}
	
	@Transient
	@JsonIgnore
	public String getHrefTarget() {
		return hrefTarget;
	}
	
	public void setHrefTarget(String hrefTarget) {
		this.hrefTarget = hrefTarget;
	}

	public void setChildren(List<Menu> children) {
		this.children = children;
	}

	public void setI18n(String i18n) {
		this.i18n = i18n;
	}
	
	@OneToMany(cascade = CascadeType.MERGE, targetEntity = Menu.class,fetch=FetchType.LAZY, mappedBy="parent")
	@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	public List<Menu> getChildren() {
		return children;
	}
	public String getI18n() {
		return i18n;
	}
	
	@ManyToOne(targetEntity = Menu.class,fetch=FetchType.LAZY)
	public Menu getParent() {
		return parent;
	}
	
	public void setParent(Menu parent) {
		this.parent = parent;
	}
	
	@Transient
	@JsonIgnore
	public List<Menu> safeChildren() {
		if (children == null) {
			children = new ArrayList<Menu>();
		}
		return children;
	}
	
	@Transient
	@Protected
	@JsonIgnore
	public Menu getChild(String name){
		for(Menu child : getChildren()){
			if (name.equals(child.getName())) {
				return child;
			}
		}
		return null;
	}
	
	@SuppressWarnings("unchecked")
	@Transient
	@JsonIgnore
	public  List<Menu> getChildren(Class<Menu> childClazz){
		List<Menu> lists = (List<Menu>) getChildren();
		Hibernate.initialize(lists);
		if (lists == null || lists.size() == 0) return null;
		List<Menu> results = new ArrayList<Menu>();
		for(Menu e : lists){
			e = (Menu) HibernateUtil.unproxy(e);
			if (childClazz.isAssignableFrom(e.getClass())){
				results.add(e);
			}
		}
		return results;
	}
	
	
	private List<Menu> childMenus;
	
	@Transient
	@Protected
	@JsonIgnore
	public  List<Menu> getChildMenus() {
		if (childMenus != null) {
			return childMenus;
		}
		
		List<Menu> children = getChildren(Menu.class);
		if (children == null || children.size() == 0) return null;
		Collections.sort(children, new Comparator<Menu>() {
			@Override
			public int compare(Menu s1, Menu s2) {
				int n1 = s1.getItemNbr() == null ? 0 : s1.getItemNbr();
				int n2 = s2.getItemNbr() == null ? 0 : s2.getItemNbr();
				return n1 - n2;
			}
		});
		childMenus = children;
		return childMenus;
	}
	
	@Transient
	@Protected
	@JsonIgnore
	public  void setChildMenus(List<Menu> childMenus) {
		this.childMenus = childMenus;
	}

	@ManyToMany(targetEntity=Page.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL)
	@JoinTable(name = "saas_menu_page")
	public List<Page> getRelevantPages() {
		return relevantPages;
	}

	public void setRelevantPages(List<Page> relevantPages) {
		this.relevantPages = relevantPages;
	}
	
	public Menu cloneIgnoreId() {
		Menu detachedMenu =  new Menu();
		if (i18n != null) {
			detachedMenu.setI18n(i18n);
		}
		detachedMenu.setItemNbr(itemNbr);
		detachedMenu.setName(name);
		return detachedMenu;
	}

	@Transient
	@JsonIgnore
	public int getDeep() {
		return deep;
	}

	public void setDeep(int deep) {
		this.deep = deep;
	}

	@Transient
	@JsonIgnore
	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}
	
}